/* vim:set noet ts=4: */
/** 
 * scim-python
 * 
 * Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 * $Id: $
 */

#include "scim-python-lookup-table.h"
#include "scim-python-attribute.h"
#if Py_UNICODE_SIZE == 2
#  include <glib.h>
#endif

struct PyLookupTableObject {
	PyObject_HEAD
	/* Type-specific fields go here. */
	PyLookupTable lookup_table;
};

PyLookupTable::PyLookupTable (PyLookupTableObject *self, unsigned int page_size)
: CommonLookupTable (page_size), self (self)
{
	Py_INCREF (self);

}

PyLookupTable::PyLookupTable (PyLookupTableObject *self, unsigned int page_size, const std::vector<WideString> &labels)
: CommonLookupTable (page_size, labels), self (self)
{
	Py_INCREF (self);
}

PyLookupTable::~PyLookupTable () 
{
	Py_DECREF (self);
}

PyDoc_STRVAR(py_set_candidate_labels__doc__,
"set_candidate_labels ((unicode, unicode, ...)) -> none\n"
"Set the strings (it must be unicode strings) to label the candidates in on page.");
PyObject *
PyLookupTable::py_set_candidate_labels (PyLookupTableObject *self, PyObject *args)
{
	PyObject *labels = NULL;
	PyObject **items = NULL;
	std::vector <WideString> _labels;
	int size;


	if (!PyArg_ParseTuple (args, "o:set_candidate_labels", &labels))
		return NULL;

	if (!PySequence_Check (labels)) {
		PyErr_SetString (PyExc_TypeError, "labels must be an array of unicode strings.");
		return NULL;
	}

	size = PySequence_Size (labels);
	items = PySequence_Fast_ITEMS (labels);

	for (int i = 0; i < size; i++ ) {
		if (!PyUnicode_Check (items[i])) {
			PyErr_SetString (PyExc_TypeError, "labels must be an array of unicode strings.");
			return NULL;
		}
#if Py_UNICODE_SIZE == 4
		_labels.push_back (WideString ((wchar_t *)PyUnicode_AS_UNICODE (items[i])));
#else
		int usize = PyUnicode_GET_SIZE (items[i]);
		gunichar *unistr = g_utf16_to_ucs4 (PyUnicode_AS_UNICODE (items[i]), usize, NULL, NULL, NULL);
		_labels.push_back (WideString ((wchar_t *)unistr));
		g_free (unistr);
#endif
	}

	self->lookup_table.set_candidate_labels (_labels);

	Py_INCREF (Py_None);
	return Py_None;

}


PyDoc_STRVAR(py_get_candidate_label__doc__,
"get_candidate_label (int) -> unicode\n"
"Return the label string of a candidate in page.");
PyObject *
PyLookupTable::py_get_candidate_label (PyLookupTableObject *self, PyObject *args)
{
	unsigned index = 0;

	if (!PyArg_ParseTuple (args, "I:get_candidate_label", &index))
		return NULL;

	WideString candidate = self->lookup_table.get_candidate_label (index);
#if Py_UNICODE_SIZE == 4
	return PyUnicode_FromUnicode ((Py_UNICODE *)candidate.c_str (), candidate.length ());
#else
	gunichar2 *utf16_str = g_ucs4_to_utf16 ((gunichar *)candidate.c_str (),
						candidate.length (), NULL, NULL, NULL);
	PyObject * result = PyUnicode_FromUnicode ((Py_UNICODE *)utf16_str, candidate.length ());
	g_free (utf16_str);
	return result;
#endif
}

PyDoc_STRVAR(py_set_page_size__doc__,
"set_page_size (int) -> none\n"
"Set the maximum page size.");
PyObject *
PyLookupTable::py_set_page_size (PyLookupTableObject *self, PyObject *args)
{
	unsigned int page_size;

	if (!PyArg_ParseTuple (args, "I:set_page_size", &page_size))
		return NULL;

	self->lookup_table.set_page_size (page_size);

	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_get_page_size__doc__,
"get_page_size () -> int\n"
"Return the maximum page size.");
PyObject *
PyLookupTable::py_get_page_size (PyLookupTableObject *self, PyObject *args)
{
	long size = self->lookup_table.get_page_size ();

	return PyInt_FromLong (size);
}

PyDoc_STRVAR(py_get_current_page_size__doc__,
"get_current_page_size () -> int\n"
"Return the current page size.");
PyObject *
PyLookupTable::py_get_current_page_size (PyLookupTableObject *self, PyObject *args)
{
	long size = self->lookup_table.get_current_page_size ();

	return PyInt_FromLong (size);
}

PyDoc_STRVAR(py_get_current_page_start__doc__,
"get_current_page_start () -> int\n"
"Return the start index of current page.");
PyObject *
PyLookupTable::py_get_current_page_start (PyLookupTableObject *self, PyObject *args)
{
	long index = self->lookup_table.get_current_page_start ();

	return PyInt_FromLong (index);
}

PyDoc_STRVAR(py_is_cursor_visible__doc__,
"is_cursor_visible () -> bool\n"
"Return true if curosr is visible.");
PyObject *
PyLookupTable::py_is_cursor_visible (PyLookupTableObject *self, PyObject *args)
{
	PyObject * result;
	if (self->lookup_table.is_cursor_visible ()) {
		result = Py_True;
	}
	else {
		result = Py_False;
	}

	Py_INCREF (result);
	return result;
}

PyDoc_STRVAR(py_is_page_size_fixed__doc__,
"is_page_size_fixed () -> bool\n"
"Return true if the page size is fixed.");
PyObject *
PyLookupTable::py_is_page_size_fixed (PyLookupTableObject *self, PyObject *args)
{
	PyObject * result;
	if (self->lookup_table.is_page_size_fixed ()) {
		result = Py_True;
	}
	else {
		result = Py_False;
	}

	Py_INCREF (result);
	return result;
}

PyDoc_STRVAR(py_get_cursor_pos__doc__,
"get_cursor_pos () -> int\n"
"Return the cursor position in the table, starting from 0.");
PyObject *
PyLookupTable::py_get_cursor_pos (PyLookupTableObject *self, PyObject *args)
{
	int pos;

	pos = self->lookup_table.get_cursor_pos ();

	return PyInt_FromLong (pos);
}

PyDoc_STRVAR(py_get_cursor_pos_in_current_page__doc__,
"get_cursor_pos_in_current_page () -> int\n"
"Return the cursor position in the current page, starting from 0.");
PyObject *
PyLookupTable::py_get_cursor_pos_in_current_page (PyLookupTableObject *self, PyObject *args)
{
	int pos;

	pos = self->lookup_table.get_cursor_pos_in_current_page ();

	return PyInt_FromLong (pos);
}

PyDoc_STRVAR(py_page_up__doc__,
"page_up () -> bool\n"
"Flip to the previous page. Return false if it's already in the first page.");
PyObject *
PyLookupTable::py_page_up (PyLookupTableObject *self, PyObject *args)
{
	PyObject *result;

	if (self->lookup_table.page_up ())
		result = Py_True;
	else
		result = Py_False;

	Py_INCREF (result);
	return result;
}

PyDoc_STRVAR(py_page_down__doc__,
"page_down () -> bool\n"
"Flip to the next page. Return false if it's already in the last page.");
PyObject *
PyLookupTable::py_page_down (PyLookupTableObject *self, PyObject *args)
{
	PyObject *result;

	if (self->lookup_table.page_down ())
		result = Py_True;
	else
		result = Py_False;

	Py_INCREF (result);
	return result;
}

PyDoc_STRVAR(py_cursor_up__doc__,
"cursor_up () -> bool\n"
"Move cursor position to the previous entry. Return false if it's already in the first entry.");
PyObject *
PyLookupTable::py_cursor_up (PyLookupTableObject *self, PyObject *args)
{
	PyObject * result;

	if (self->lookup_table.cursor_up ()) {
		result = Py_True;
	}
	else {
		result = Py_False;
	}

	Py_INCREF (result);
	return result;
}

PyDoc_STRVAR(py_cursor_down__doc__,
"cursor_down () -> bool\n"
"Move cursor position to the next entry. Return false if it's already in the last entry.");
PyObject *
PyLookupTable::py_cursor_down (PyLookupTableObject *self, PyObject *args)
{
	PyObject * result;

	if (self->lookup_table.cursor_down ()) {
		result = Py_True;
	}
	else {
		result = Py_False;
	}

	Py_INCREF (result);
	return result;
}

PyDoc_STRVAR(py_show_cursor__doc__,
"show_cursor (bool) -> none\n"
"Set the cursor visibility.");
PyObject *
PyLookupTable::py_show_cursor (PyLookupTableObject *self, PyObject *args)
{
	unsigned int show = 1;

	if (!PyArg_ParseTuple (args, "|I:show_cursor", &show))
		return NULL;

	self->lookup_table.show_cursor (show);

	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_fix_page_size__doc__,
"fix_page_size (bool) -> none\n"
"Set the page size to be fixed, aka. prevent from being changed by FrontEnd.");
PyObject *
PyLookupTable::py_fix_page_size (PyLookupTableObject *self, PyObject *args)
{
	unsigned int fixed = 1;

	if (!PyArg_ParseTuple (args, "|I:fix_page_size", &fixed))
		return NULL;

	self->lookup_table.fix_page_size (fixed);

	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_set_cursor_pos__doc__,
"set_cursor_pos (int) -> none\n"
"Set the cursor position.");
PyObject *
PyLookupTable::py_set_cursor_pos (PyLookupTableObject *self, PyObject *args)
{
	unsigned int pos;

	if (!PyArg_ParseTuple (args, "I:set_cursor_pos", &pos))
		return NULL;

	self->lookup_table.set_cursor_pos (pos);

	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_set_cursor_pos_in_current_page__doc__,
"set_cursor_pos_in_current_page (int) -> none\n"
"Set the cursor position in current page.");
PyObject *
PyLookupTable::py_set_cursor_pos_in_current_page (PyLookupTableObject *self, PyObject *args)
{
	unsigned int pos;

	if (!PyArg_ParseTuple (args, "I:set_cursor_pos_in_current_pos", &pos))
		return NULL;

	self->lookup_table.set_cursor_pos_in_current_page (pos);

	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_get_candidate_in_current_page__doc__,
"get_candidate_in_current_page (int) -> unicode\n"
"Return the content of this candidate.");
PyObject *
PyLookupTable::py_get_candidate_in_current_page (PyLookupTableObject *self, PyObject *args)
{
	unsigned index = 0;

	if (!PyArg_ParseTuple (args, "I:get_candidate_in_current_page", &index))
		return NULL;

	WideString candidate = self->lookup_table.get_candidate_in_current_page (index);
#if Py_UNICODE_SIZE == 4
	return PyUnicode_FromUnicode ((Py_UNICODE *)candidate.c_str (), candidate.length ());
#else
	gunichar2 *utf16_str = g_ucs4_to_utf16 ((gunichar *)candidate.c_str (),
						candidate.length (), NULL, NULL, NULL);
	PyObject * result = PyUnicode_FromUnicode ((Py_UNICODE *)utf16_str, candidate.length ());
	g_free (utf16_str);
	return result;
#endif
}

PyDoc_STRVAR(py_get_candidate__doc__,
"get_candidate (int) -> unicode\n"
"Return the content of this candidate.");
PyObject *
PyLookupTable::py_get_candidate (PyLookupTableObject *self, PyObject *args)
{
	unsigned index = 0;

	if (!PyArg_ParseTuple (args, "I:get_candidate", &index))
		return NULL;

	WideString candidate = self->lookup_table.get_candidate (index);
#if Py_UNICODE_SIZE == 4
	return PyUnicode_FromUnicode ((Py_UNICODE *)candidate.c_str (), candidate.length ());
#else
	gunichar2 *utf16_str = g_ucs4_to_utf16 ((gunichar *)candidate.c_str (),
						candidate.length (), NULL, NULL, NULL);
	PyObject * result = PyUnicode_FromUnicode ((Py_UNICODE *)utf16_str, candidate.length ());
	g_free (utf16_str);
	return result;
#endif
}


PyDoc_STRVAR(py_get_attributes__doc__,
"get_attributes (int) -> list\n"
"Return a list holding the attributes of this candidates.");
PyObject *
PyLookupTable::py_get_attributes (PyLookupTableObject *self, PyObject *args)
{
#if 0
	PyObject *result;

	unsigned index = 0;

	if (!PyArg_ParseTuple (args, "I:get_attributes", &index))
		return NULL;

	AttributeList atts = self->lookup_table.get_attributes (index)

	result = PyTuple_New ();
#endif
	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_get_attributes_in_current_page__doc__,
"get_attributes_in_current_page (int) -> list\n"
"Return a list holding the attributes of this candidates.");
PyObject *
PyLookupTable::py_get_attributes_in_current_page (PyLookupTableObject *self, PyObject *args)
{
#if 0
	PyObject *result;

	unsigned index = 0;

	if (!PyArg_ParseTuple (args, "I:get_attributes", &index))
		return NULL;

	AttributeList atts = self->lookup_table.get_attributes (index)

	result = PyTuple_New ();
#endif
	Py_INCREF (Py_None);
	return Py_None;
}


PyDoc_STRVAR(py_number_of_candidates__doc__,
"number_of_candidates () -> int\n"
"Return the number of candidates in the table.");
PyObject *
PyLookupTable::py_number_of_candidates (PyLookupTableObject *self, PyObject *args)
{
	uint32 number;

	number = self->lookup_table.number_of_candidates ();

	return PyInt_FromLong (number);
}

PyDoc_STRVAR(py_clear__doc__,
"clear () -> none\n"
"Clear the table.");
PyObject *
PyLookupTable::py_clear (PyLookupTableObject *self, PyObject *args)
{
	self->lookup_table.clear ();
	Py_INCREF (Py_None);
	return Py_None;
}

PyDoc_STRVAR(py_append_candidate__doc__,
"append_candidate (unicode, list) -> bool\n"
"Append a candidate string to the table, the second is a list holding the attributes.\n"
"Return true if success.");
PyObject *
PyLookupTable::py_append_candidate (PyLookupTableObject *self, PyObject *args)
{
	Py_UNICODE *candidate = NULL;
	PyObject *pAttrs = NULL;
	PyObject *result = Py_False;

#if Py_UNICODE_SIZE == 4
	if (!PyArg_ParseTuple (args, "u|O:append_candidate", &candidate, &pAttrs))
		return NULL;

	if (self->lookup_table.append_candidate (WideString ((wchar_t *)candidate),
								Attributes_FromTupleOrList (pAttrs)))
	{
		result = Py_True;
	}
#else
	int size = 0;
	gunichar *unistr = NULL;

	if (!PyArg_ParseTuple (args, "u#|O:append_candidate", &candidate, &size, &pAttrs))
		return NULL;
	unistr = g_utf16_to_ucs4 (candidate, size, NULL, NULL, NULL);

	if (self->lookup_table.append_candidate (WideString ((wchar_t *)unistr),
								Attributes_FromTupleOrList (pAttrs)))
	{
		result = Py_True;
	}
	g_free (unistr);
#endif

	Py_INCREF (result);
	return result;
}










PyMethodDef
PyLookupTable::py_methods[] = {
#define ENTRY(name, flags) {#name, (PyCFunction)PyLookupTable::py_##name, flags, py_##name##__doc__}
	ENTRY (set_candidate_labels, METH_VARARGS),
	ENTRY (get_candidate_label, METH_VARARGS),
	ENTRY (set_page_size, METH_VARARGS),
	ENTRY (get_page_size, METH_NOARGS),
	ENTRY (get_current_page_size, METH_NOARGS),
	ENTRY (get_current_page_start, METH_NOARGS),
	ENTRY (is_cursor_visible, METH_NOARGS),
	ENTRY (is_page_size_fixed, METH_NOARGS),
	ENTRY (get_cursor_pos, METH_NOARGS),
	ENTRY (get_cursor_pos_in_current_page, METH_NOARGS),
	ENTRY (page_up, METH_NOARGS),
	ENTRY (page_down, METH_NOARGS),
	ENTRY (cursor_up, METH_NOARGS),
	ENTRY (cursor_down, METH_NOARGS),
	ENTRY (show_cursor, METH_VARARGS),
	ENTRY (fix_page_size, METH_VARARGS),
	ENTRY (set_cursor_pos, METH_VARARGS),
	ENTRY (set_cursor_pos_in_current_page, METH_VARARGS),
	ENTRY (get_candidate_in_current_page, METH_VARARGS),
	ENTRY (get_attributes_in_current_page, METH_VARARGS),
	ENTRY (get_candidate, METH_VARARGS),
	ENTRY (get_attributes, METH_VARARGS),
	ENTRY (number_of_candidates, METH_NOARGS),
	ENTRY (clear, METH_NOARGS),
	ENTRY (append_candidate, METH_VARARGS),
#undef ENTRY
		{ NULL }
};

PyObject *
PyLookupTable::py_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	PyLookupTable *self;

	self = (PyLookupTable *)type->tp_alloc (type, 0);
	return (PyObject *)self;
}

int
PyLookupTable::py_init (PyLookupTableObject *self, PyObject *args, PyObject *kwds)
{
	unsigned int page_size = 10;

	if (!PyArg_ParseTuple (args, "|I:__init__", &page_size))
		return -1;

	new (&self->lookup_table) PyLookupTable (self, page_size);

	return 0;
}

void
PyLookupTable::py_dealloc (PyLookupTableObject *self)
{
	self->lookup_table.~LookupTable ();
	((PyObject *)self)->ob_type->tp_free (self);
}

const PyLookupTable &
PyLookupTable::from_pyobject (PyObject *object)
{
    return ((PyLookupTableObject *)object)->lookup_table;
}

PyTypeObject PyLookupTableType = {
	PyObject_HEAD_INIT (NULL)
	0,						 			/*ob_size*/
	"scim.LookupTable", 				/*tp_name*/
	sizeof (PyLookupTableObject), 		/*tp_basicsize*/
	0,						 			/*tp_itemsize*/
	(destructor)PyLookupTable::py_dealloc,	/*tp_dealloc*/
	0,			  						/*tp_print*/
	0,						 			/*tp_getattr*/
	0,									/*tp_setattr*/
	0,									/*tp_compare*/
	0,			  						/*tp_repr*/
	0,									/*tp_as_number*/
	0,			  						/*tp_as_sequence*/
	0,									/*tp_as_mapping*/
	0,			  						/*tp_hash */
	0,									/*tp_call*/
	0,		  							/*tp_str*/
	0,					   				/*tp_getattro*/
	0,									/*tp_setattro*/
	0,					 				/*tp_as_buffer*/
	Py_TPFLAGS_DEFAULT | 
	Py_TPFLAGS_BASETYPE,				/*tp_flags*/
	"PyLookupTable objects",		 	/* tp_doc */
	0,					   				/* tp_traverse */
	0,					   				/* tp_clear */
	0,					   				/* tp_richcompare */
	0,					   				/* tp_weaklistoffset */
	0,					   				/* tp_iter */
	0,					   				/* tp_iternext */
	PyLookupTable::py_methods,			/* tp_methods */
	0,			 						/* tp_members */
	0,						 			/* tp_getset */
	0,						 			/* tp_base */
	0,						 			/* tp_dict */
	0,						 			/* tp_descr_get */
	0,						 			/* tp_descr_set */
	0,									/* tp_dictoffset */
	(initproc)PyLookupTable::py_init,	/* tp_init */
	0,						 			/* tp_alloc */
	PyLookupTable::py_new,				/* tp_new */
	PyObject_Del,				 		/* tp_free */
};

void init_lookup_table (PyObject *module)
{
	if (PyType_Ready (&PyLookupTableType) < 0)
		return;

	Py_INCREF (&PyLookupTableType);
	PyModule_AddObject (module, "LookupTable", (PyObject *)&PyLookupTableType);
}
